package com.share.infrastructure.persistent.repository;

import com.share.domain.strategy.model.entity.StrategyAwardEntity;
import com.share.domain.strategy.model.entity.StrategyEntity;
import com.share.domain.strategy.model.entity.StrategyRuleEntity;
import com.share.domain.strategy.model.vo.*;
import com.share.domain.strategy.repository.IStrategyRepository;
import com.share.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
import com.share.infrastructure.persistent.dao.*;
import com.share.infrastructure.persistent.po.*;
import com.share.infrastructure.persistent.redis.IRedisService;
import com.share.types.common.Constants;
import com.share.types.enums.ResponseCode;
import com.share.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RMap;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 策略仓储实现
 */
@Repository
@Slf4j
public class StrategyRepository implements IStrategyRepository {

    @Resource
    private IStrategyRuleDao strategyRuleDao;

    @Resource
    private IStrategyDao strategyDao;

    @Resource
    private IStrategyAwardDao strategyAwardDao;

    @Resource
    private IRedisService redisService;

    @Resource
    private IRuleTreeDao ruleTreeDao;

    @Resource
    private IRuleTreeNodeDao ruleTreeNodeDao;

    @Resource
    private IRuleTreeNodeLineDao ruleTreeNodeLineDao;

    @Resource
    private IRaffleActivityDao raffleActivityDao;

    @Resource
    private IRaffleActivityAccountDayDao raffleActivityAccountDayDao;

    @Resource
    private IRaffleActivityAccountDao raffleActivityAccountDao;

    @Override
    public List<StrategyAwardEntity> queryStrategyAwardList(Long strategyId) {
        //先从redis中查询策略奖品列表
        //构造key
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_LIST_KEY + strategyId;
        //从redis查询
        List<StrategyAwardEntity> strategyAwardEntities = redisService.getValue(cacheKey);
        //判断是否为空
        if(strategyAwardEntities!=null && !strategyAwardEntities.isEmpty()){
            //返回策略奖品列表
            return strategyAwardEntities;
        }
        //redis中为空无数据 从数据库查询
        //获得StrategyAward类型数据 需要转换
        List<StrategyAward> strategyAwards = strategyAwardDao.queryStrategyAwardListByStrategyId(strategyId);
        strategyAwardEntities = new ArrayList<>(strategyAwards.size());
        for (StrategyAward strategyAward:strategyAwards){
            StrategyAwardEntity strategyAwardEntity = StrategyAwardEntity.builder()
                    .strategyId(strategyAward.getStrategyId())
                    .awardId(strategyAward.getAwardId())
                    .awardCount(strategyAward.getAwardCount())
                    .awardCountSurplus(strategyAward.getAwardCountSurplus())
                    .awardRate(strategyAward.getAwardRate())
                    .sort(strategyAward.getSort())
                    .awardTitle(strategyAward.getAwardTitle())
                    .awardSubTitle(strategyAward.getAwardTitle())
                    .ruleModels(strategyAward.getRuleModels())
                    .build();
            strategyAwardEntities.add(strategyAwardEntity);
        }
        //将数据放入redis中
        redisService.setValue(cacheKey,strategyAwardEntities);

        return strategyAwardEntities;

    }

    /**
     * 存储策略随机数范围值和概率查找表到redis
     * @param key
     * @param rateRange
     * @param shuffleStrategyAwardSearchRateTables
     */
    @Override
    public void storeStrategyAwardSearchRateTables(String key, int rateRange, Map<Integer, Integer> shuffleStrategyAwardSearchRateTables) {

        //范围值 如生成1000以内的随机数
        String rateRangekey = Constants.RedisKey.STRATEGY_RATE_RANGE_KEY + key;
        redisService.setValue(rateRangekey , rateRange);
        //存储概率查找表
        String rateTableKey = Constants.RedisKey.STRATEGY_RATE_TABLE_KEY + key;
        RMap<Object, Object> rateTable = redisService.getMap(rateTableKey);
        rateTable.putAll(shuffleStrategyAwardSearchRateTables);
    }

    @Override
    public Integer getRateRange(Long strategyId) {
        return getRateRange(String.valueOf(strategyId));
    }

    @Override
    public Integer getRateRange(String key) {
        String cacheKey = Constants.RedisKey.STRATEGY_RATE_RANGE_KEY + key;
        if(!redisService.isExists(cacheKey)){
            throw new AppException(ResponseCode.UN_ASSEMBLED_STRATEGY_ARMORY.getCode(),cacheKey+Constants.COLON+ResponseCode.UN_ASSEMBLED_STRATEGY_ARMORY.getInfo());
        }
        return redisService.getValue(cacheKey);
    }

    @Override
    public Integer getStrategyAwardAssemble(String key, int rateKey) {
        String rateTable = Constants.RedisKey.STRATEGY_RATE_TABLE_KEY + key;
        return redisService.getFromMap(rateTable, rateKey);
    }

    @Override
    public StrategyEntity queryStrategyEntitiesByStrategyId(Long strategyId) {
        //构建策略查询key
        String strategyKey = Constants.RedisKey.STRATEGY_KEY + strategyId;
        //redis中查询
        StrategyEntity strategyEntity = redisService.getValue(strategyKey);
        ///有则反 无则数据库
        if(strategyEntity!=null)return strategyEntity;
        //数据库查询
        Strategy strategy = strategyDao.queryStrategyByStrategyId(strategyId);
        //构造entity
        StrategyEntity strategyEntity1 = StrategyEntity.builder()
                .strategyId(strategy.getStrategyId())
                .strategyDesc(strategy.getStrategyDesc())
                .ruleModels(strategy.getRuleModels())
                .build();
        //存入redis
        redisService.setValue(strategyKey,strategyEntity1);
        return strategyEntity1;
    }

    @Override
    public StrategyRuleEntity queryStrategyRule(Long strategyId, String ruleModel) {
        StrategyRule strategyRuleReq =new StrategyRule();
        strategyRuleReq.setStrategyId(strategyId);
        strategyRuleReq.setRuleModel(ruleModel);
        StrategyRule strategyRule = strategyRuleDao.queryStrategyRule(strategyRuleReq);
        return StrategyRuleEntity.builder()
                .strategyId(strategyRule.getStrategyId())
                .awardId(strategyRule.getAwardId())
                .ruleType(strategyRule.getRuleType())
                .ruleModel(strategyRule.getRuleModel())
                .ruleValue(strategyRule.getRuleValue())
                .ruleDesc(strategyRule.getRuleDesc())
                .build();
    }

    @Override
    public String queryStrategyRuleValue(Long strategyId, Integer awardId, String ruleModel) {
        StrategyRule strategyRule =new StrategyRule();
        strategyRule.setStrategyId(strategyId);
        strategyRule.setAwardId(awardId);
        strategyRule.setRuleModel(ruleModel);
        return strategyRuleDao.queryStrategyRuleValue(strategyRule);
    }

    @Override
    public String queryStrategyRuleValue(Long strategyId, String ruleModel) {
        return queryStrategyRuleValue(strategyId,null,ruleModel);

    }

    @Override
    public StrategyAwardRuleModelVo queryAwardRuleModel(Long strategyId, Integer awardId) {
        StrategyAward strategyAward = new StrategyAward();
        strategyAward.setStrategyId(strategyId);
        strategyAward.setAwardId(awardId);
        String ruleModel = strategyAwardDao.queryStrategyAwardRuleModels(strategyAward);
        return StrategyAwardRuleModelVo.builder()
                .ruleModels(ruleModel)
                .build();

    }

    @Override
    public RuleTreeVo queryRuleTreeVoByTreeId(String treeId) {
        //优先从缓存查询
        String cacheKey =Constants.RedisKey.RULE_TREE_VO_KEY+treeId;
        RuleTreeVo ruleTreeVoCache = redisService.getValue(cacheKey);
        if(null!=ruleTreeVoCache)return ruleTreeVoCache;
        RuleTree ruleTree = ruleTreeDao.queryRuleTreeByTreeId(treeId);
        if(null==ruleTree)return null;
        List<RuleTreeNode> ruleTreeNodes = ruleTreeNodeDao.queryRuleTreeNodeListByTreeId(treeId);
        List<RuleTreeNodeLine> ruleTreeNodeLines = ruleTreeNodeLineDao.queryRuleTreeNodeLineListByTreeId(treeId);

        //1.tree node line 转换map结构
        Map<String, List<RuleTreeNodeLineVo>> ruleTreeNodeLineMap = new HashMap<>();
        for (RuleTreeNodeLine ruleTreeNodeLine : ruleTreeNodeLines) {
            RuleTreeNodeLineVo ruleTreeNodeLineVo = RuleTreeNodeLineVo.builder()
                    .treeId(ruleTreeNodeLine.getTreeId())
                    .ruleNodeFrom(ruleTreeNodeLine.getRuleNodeFrom())
                    .ruleNodeTo(ruleTreeNodeLine.getRuleNodeTo())
                    .ruleLimitType(RuleLimitTypeVo.valueOf(ruleTreeNodeLine.getRuleLimitType()))
                    .ruleLimitValue(RuleLogicCheckTypeVo.valueOf(ruleTreeNodeLine.getRuleLimitValue()))
                    .build();
            List<RuleTreeNodeLineVo> ruleTreeNodeLineVoList = ruleTreeNodeLineMap.computeIfAbsent(ruleTreeNodeLine.getRuleNodeFrom(), k -> new ArrayList<>());
            ruleTreeNodeLineVoList.add(ruleTreeNodeLineVo);
        }

        //2.tree node 转换为map
        Map<String, RuleTreeNodeVo> treeNodeMap = new HashMap<>();
        for (RuleTreeNode ruleTreeNode : ruleTreeNodes) {
            RuleTreeNodeVo ruleTreeNodeVo = RuleTreeNodeVo.builder()
                    .treeId(ruleTreeNode.getTreeId())
                    .ruleKey(ruleTreeNode.getRuleKey())
                    .ruleDesc(ruleTreeNode.getRuleDesc())
                    .ruleValue(ruleTreeNode.getRuleValue())
                    .treeNodeLineVoList(ruleTreeNodeLineMap.get(ruleTreeNode.getRuleKey()))
                    .build();
            treeNodeMap.put(ruleTreeNode.getRuleKey(), ruleTreeNodeVo);
        }

        //3.构建rule tree
        RuleTreeVo ruleTreeVoDB = RuleTreeVo.builder()
                .treeId(ruleTree.getTreeId())
                .treeName(ruleTree.getTreeName())
                .treeDesc(ruleTree.getTreeDesc())
                .treeRootRuleNode(ruleTree.getTreeRootRuleKey())
                .treeNodeMap(treeNodeMap)
                .build();

        //存入缓存
        redisService.setValue(cacheKey,ruleTreeVoDB);

        return ruleTreeVoDB;
    }

    @Override
    public StrategyAwardRuleModelVo queryStrategyAwardRuleModelVo(Long strategyId, Integer awardId) {
        StrategyAward strategyAward = new StrategyAward();
        strategyAward.setAwardId(awardId);
        strategyAward.setStrategyId(strategyId);
        String ruleModels = strategyAwardDao.queryStrategyAwardRuleModels(strategyAward);
        if(null==ruleModels)return null;
        return StrategyAwardRuleModelVo.builder()
                .ruleModels(ruleModels)
                .build();

    }

    @Override
    public void cacheStrategyAwardCount(String cacheKey, Integer awardCountSurplus) {
        if(redisService.isExists(cacheKey))return;
        redisService.setAtomicLong(cacheKey,awardCountSurplus);
    }

    @Override
    public boolean subtractionAwardStock(String cacheKey, Date endDateTime) {
        long surplus = redisService.decr(cacheKey);
        if(surplus<0){
            redisService.setValue(cacheKey,null);
            return false;
        }
        String lockKey = cacheKey + Constants.UNDERLINE + surplus;
        boolean lock;
        if(null!=endDateTime){
            long expireMillis = endDateTime.getTime() - System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1);
            lock = redisService.setNx(lockKey,expireMillis);
        }else {
            lock = redisService.setNx(lockKey);
        }
        if(!lock){
            log.info("加锁失败，该锁已存在：{}",lockKey);
        }
        return lock;
    }

    @Override
    public void awardStockConsumeSendQueue(StrategyAwardStockKeyVo strategyAwardStockKeyVo) {
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_QUEUE_KEY;
        RBlockingQueue<Object> blockingQueue = redisService.getBlockingQueue(cacheKey);
        RDelayedQueue<Object> delayedQueue = redisService.getDelayedQueue(blockingQueue);
        delayedQueue.offer(strategyAwardStockKeyVo,3, TimeUnit.SECONDS);

    }

    @Override
    public StrategyAwardStockKeyVo takeQueueValue() {
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_QUEUE_KEY;
        RBlockingQueue<StrategyAwardStockKeyVo> destinationQueue = redisService.getBlockingQueue(cacheKey);
        return destinationQueue.poll();

    }

    @Override
    public void updateStrategyAwardStock(Long strategyId, Integer awardId) {
        StrategyAward strategyAward = new StrategyAward();
        strategyAward.setStrategyId(strategyId);
        strategyAward.setAwardId(awardId);
        strategyAwardDao.updateStrategyAwardStock(strategyAward);
    }

    @Override
    public StrategyAwardEntity queryStrategyAwardEntity(Long strategyId, Integer awardId) {
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_KEY + strategyId + Constants.UNDERLINE + awardId;
        StrategyAwardEntity strategyAwardEntity = redisService.getValue(cacheKey);
        if(null!=strategyAwardEntity)return strategyAwardEntity;

        StrategyAward strategyAwardReq = new StrategyAward();
        strategyAwardReq.setAwardId(awardId);
        strategyAwardReq.setStrategyId(strategyId);
        StrategyAward strategyAwardRes = strategyAwardDao.queryStrategyAward(strategyAwardReq);
        strategyAwardEntity = StrategyAwardEntity.builder()
                .strategyId(strategyAwardRes.getStrategyId())
                .awardId(strategyAwardRes.getAwardId())
                .awardTitle(strategyAwardRes.getAwardTitle())
                .awardSubTitle(strategyAwardRes.getAwardSubtitle())
                .awardCount(strategyAwardRes.getAwardCount())
                .awardCountSurplus(strategyAwardRes.getAwardCountSurplus())
                .awardRate(strategyAwardRes.getAwardRate())
                .sort(strategyAwardRes.getSort())
                .build();
        redisService.setValue(cacheKey,strategyAwardEntity);
        return strategyAwardEntity;
    }

    @Override
    public Long queryStrategyIdByActivityId(Long activityId) {
        return raffleActivityDao.queryStrategyIdByActivityId(activityId);

    }

    @Override
    public int queryTodayUserRaffleCount(String userId, Long strategyId) {
        Long activityId = raffleActivityDao.queryActivityIdByStrategyId(strategyId);
        RaffleActivityAccountDay raffleActivityAccountDayReq = new RaffleActivityAccountDay();
        raffleActivityAccountDayReq.setUserId(userId);
        raffleActivityAccountDayReq.setActivityId(activityId);
        raffleActivityAccountDayReq.setDay(raffleActivityAccountDayReq.currentDay());

        RaffleActivityAccountDay raffleActivityAccountDay = raffleActivityAccountDayDao.queryActivityAccountDayByUserId(raffleActivityAccountDayReq);
        if(null==raffleActivityAccountDay)return 0;
        return raffleActivityAccountDay.getDayCount()-raffleActivityAccountDay.getDayCountSurplus();

    }

    @Override
    public Map<String, Integer> queryAwardRuleLockCount(String[] treeIds) {
        if(null==treeIds||0==treeIds.length)return new HashMap<>();
        List<RuleTreeNode> ruleTreeNodes = ruleTreeNodeDao.queryRuleLocks(treeIds);
        Map<String, Integer> resultMap = new HashMap<>();
        for (RuleTreeNode node : ruleTreeNodes) {
            String treeId = node.getTreeId();
            Integer ruleValue = Integer.valueOf(node.getRuleValue());
            resultMap.put(treeId,ruleValue);
        }
        return resultMap;
    }

    @Override
    public Integer queryActivityAccountTotalUserCount(String userId, Long strategyId) {
        Long activityId = raffleActivityDao.queryActivityIdByStrategyId(strategyId);
        RaffleActivityAccount raffleActivityAccount = raffleActivityAccountDao.queryActivityAccountByUserId(RaffleActivityAccount.builder()
                .activityId(activityId)
                .userId(userId)
                .build());
        return raffleActivityAccount.getTotalCount() - raffleActivityAccount.getTotalCountSurplus();

    }

    @Override
    public List<RuleWeightVo> queryRuleWeight(Long strategyId) {
        String cacheKey = Constants.RedisKey.STRATEGY_RULE_WEIGHT_KEY + strategyId;
        List<RuleWeightVo> ruleWeightVoList = redisService.getValue(cacheKey);
        if(null!=ruleWeightVoList && !ruleWeightVoList.isEmpty())return ruleWeightVoList;
        ruleWeightVoList = new ArrayList<>();
        //1.查询权重信息
        StrategyRule strategyRuleReq = new StrategyRule();
        strategyRuleReq.setStrategyId(strategyId);
        strategyRuleReq.setRuleModel(DefaultChainFactory.LogicModel.RULE_WEIGHT.getCode());
        String ruleValue = strategyRuleDao.queryStrategyRuleValue(strategyRuleReq);

        //借助实体的方法查出配置值
        StrategyRuleEntity strategyRuleEntity = new StrategyRuleEntity();
        strategyRuleEntity.setRuleModel(DefaultChainFactory.LogicModel.RULE_WEIGHT.getCode());
        strategyRuleEntity.setRuleValue(ruleValue);
        Map<String, List<Integer>> ruleWeightValues = strategyRuleEntity.getRuleWeightValues();

        //循环变量
        for (String key : ruleWeightValues.keySet()) {
            List<Integer> awardIds = ruleWeightValues.get(key);
            List<RuleWeightVo.Award> awardList = new ArrayList<>();
            //查询每个配值的对应奖品
            for (Integer awardId : awardIds) {
                StrategyAward strategyAwardReq =new StrategyAward();
                strategyAwardReq.setAwardId(awardId);
                strategyAwardReq.setStrategyId(strategyId);
                StrategyAward strategyAward = strategyAwardDao.queryStrategyAward(strategyAwardReq);

                awardList.add(RuleWeightVo.Award.builder()
                        .awardId(strategyAward.getAwardId())
                        .awardTitle(strategyAward.getAwardTitle())
                        .build());
            }
            ruleWeightVoList.add(RuleWeightVo.builder()
                    .awardList(awardList)
                    .awardIds(awardIds)
                    .ruleValue(ruleValue)
                    .weight(Integer.valueOf(key.split(Constants.COLON)[0]))
                    .build());
        }
        redisService.setValue(cacheKey,ruleWeightVoList);
        return ruleWeightVoList;
    }
}
